﻿#region All usings
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
#endregion


namespace senSimulator.DrawableComponents.TrafficLights
{
    class TrafficLight : DrawableComponent
    {
        #region Declaration of variables and basic components
        /// <summary>
        /// Basic variables for the traffic light:
        ///  - name (cnw = car from north to west)
        ///  - Type traffic light (bus/pedestrian/car/bicycle)
        ///  - traffic queue (all traffic that is waiting for the "green light"
        ///  - sprite effects: flip a texture or not
        ///  - state (1 = red, 2 = orange, 3 = green for example)
        ///  - blinking: on/off switching for special states
        ///  - blinking counter: to keep track of the number of frames per blink
        /// </summary>
        protected string _trafficLightID            = "";
        protected List<Traffic> _trafficLightQueue  = null;
        protected SpriteEffects _spriteEffects      = SpriteEffects.None;

        // Different states for the traffic light
        protected int _trafficLightState            = 1;
        protected int _blinkLightState              = 0;
        protected int _tempStateStorage             = 0;
        protected int _trafficLightQueueLength      = 0;
        protected bool _errorAndOffstate            = false;
        protected bool _blinking                    = false;
        protected int _blinkingCounter              = 0;

        // Traffic settings (spawning and distance towards the cross road)
        protected int _distanceToTrafficLight       = 60;
        protected Vector2 _spawnPosition;
        protected Vector2 _distanceVectorToTrafficLight;


        /// <summary>
        /// Constructor
        /// </summary>
        public TrafficLight(Game game, Vector2 spawnPosition, Texture2D[] textureNames, Vector2[] textureCenters, int posX, int posY, int rotationInDegrees, string trafficLightID, int distanceToTrafficLight)
            : base(game, textureNames, textureCenters, posX, posY, rotationInDegrees)
        {
            // Set the variables
            _spawnPosition          = spawnPosition;
            _trafficLightID         = trafficLightID;
            _trafficLightQueue      = new List<Traffic>();
            _trafficLightState      = _textures.Length - 1;
            _blinkLightState        = textureNames.Length - 1;
            _distanceToTrafficLight = distanceToTrafficLight;
            InitializeDistanceVectorToTrafficLight();
        }


        /// <summary>
        /// Constructor
        /// </summary>
        public TrafficLight(Game game, Vector2 spawnPosition, Texture2D[] textureNames, Vector2[] textureCenters, int posX, int posY, int rotationInDegrees, SpriteEffects spriteEffect, string trafficLightID, int distanceToTrafficLight)
            : base(game, textureNames, textureCenters, posX, posY, rotationInDegrees)
        {
            _spawnPosition          = spawnPosition;
            _trafficLightID         = trafficLightID;
            _trafficLightQueue      = new List<Traffic>();
            _spriteEffects          = spriteEffect;
            _trafficLightState      = _textures.Length - 1;
            _blinkLightState        = textureNames.Length - 1;
            _distanceToTrafficLight = distanceToTrafficLight;
            InitializeDistanceVectorToTrafficLight();
        }


        /// <summary>
        /// Initialize the correct (starting) queue length
        /// </summary>
        protected virtual void InitializeDistanceVectorToTrafficLight()
        {
            _distanceVectorToTrafficLight = new Vector2();

            // Set the correct queue length (start position)
            if (getName().Last().Equals('6')) //bus
            {
                if (_spawnPosition.X < 0)
                    _distanceVectorToTrafficLight.X = -_distanceToTrafficLight - 12;
                else if (_spawnPosition.X > 0)
                    _distanceVectorToTrafficLight.X = _distanceToTrafficLight + 12;
                else
                    _distanceVectorToTrafficLight.X = 0;

                if (_spawnPosition.Y < 0)
                    _distanceVectorToTrafficLight.Y = -_distanceToTrafficLight - 12;
                else if (_spawnPosition.Y > 0)
                    _distanceVectorToTrafficLight.Y = _distanceToTrafficLight + 12;
                else
                    _distanceVectorToTrafficLight.Y = 0;
            }
            else
            {
                if (_spawnPosition.X < 0)
                    _distanceVectorToTrafficLight.X = -_distanceToTrafficLight;
                else if (_spawnPosition.X > 0)
                    _distanceVectorToTrafficLight.X = _distanceToTrafficLight;
                else
                    _distanceVectorToTrafficLight.X = 0;

                if (_spawnPosition.Y < 0)
                    _distanceVectorToTrafficLight.Y = -_distanceToTrafficLight;
                else if (_spawnPosition.Y > 0)
                    _distanceVectorToTrafficLight.Y = _distanceToTrafficLight;
                else
                    _distanceVectorToTrafficLight.Y = 0;
            }
        }
        #endregion


        #region All abstract functionality, which differs per traffic light
        /// <summary>
        /// ABSTRACT Traffic light state
        /// </summary>
        /// <param name="state">New state of the traffic light</param>
        public virtual void ChangeTrafficLightState(int state)
        {
            _trafficLightState = state;
        }


        /// <summary>
        /// ABSTRACT Traffic light state
        /// </summary>
        /// <param name="state">New state of the traffic light</param>
        public virtual void ChangeTrafficLightState(int state, bool blink)
        {
            _trafficLightState = state;
            _blinking = blink;
        }


        /// <summary>
        /// Abstract method to move the traffic
        /// </summary>
        public virtual void MoveTraffic(Controller controller)
        {}
        #endregion


        #region All GLOBAL functionality for the traffic light
        /// <summary>
        /// Gets the Id of the trafficLight
        /// </summary>
        /// <returns>TrafficLightID</returns>
        public string getName()
        {
            return _trafficLightID;
        }


        /// <summary>
        /// Switch the traffic light off
        /// </summary>
        public void SwitchOff()
        {
            _trafficLightState = _textures.Length - 1;
            _blinking = false;
            _errorAndOffstate = true;
        }


        /// <summary>
        /// Switch the traffic light on
        /// </summary>
        public void SwitchOn()
        {
            _trafficLightState = 1;
        }


        /// <summary>
        /// Add a traffic item to the traffic (light) queue
        /// </summary>
        /// <param name="traffic">A traffic type</param>
        public virtual void AddTraffic(Traffic traffic, CrossRoadDirection toWardsCrossRoad)
        {            
            // Spawn the car outside the level and after a vehicle
            Traffic previousTraffic = null;
            if (_trafficLightQueueLength > 0)
            {
                lock (_trafficLightQueue)
                {
                    previousTraffic = _trafficLightQueue.Last();
                }
            }

            if (previousTraffic != null && !previousTraffic.SensorTriggered())
            {   // Check if the traffic needs to spawn outside the screen
                Vector2 v = new Vector2(previousTraffic.GetPosition().X + _distanceVectorToTrafficLight.X, previousTraffic.GetPosition().Y + _distanceVectorToTrafficLight.Y);
                if ((v.X > (0 - traffic.GetLength() / 2) && v.X < (768 + traffic.GetLength() / 2)) && (v.Y > (0 - traffic.GetLength() / 2) && v.Y < (768 + traffic.GetLength() / 2)))
                    traffic.SetPosition(_texturePosition.X + _spawnPosition.X, _texturePosition.Y + _spawnPosition.Y, false);                    
                else
                    traffic.SetPosition(v.X, v.Y, false);
            }
            else
                traffic.SetPosition(_texturePosition.X + _spawnPosition.X, _texturePosition.Y + _spawnPosition.Y, false);

            // Create all way points for the traffic
            Vector2 _beforeTrafficlight = new Vector2(_texturePosition.X + _distanceVectorToTrafficLight.X, _texturePosition.Y + _distanceVectorToTrafficLight.Y);
            Vector2 _sensorLocation = new Vector2(_texturePosition.X, _texturePosition.Y);
            Vector2 _exitLanePosition = toWardsCrossRoad.GetNearestExitLane(_texturePosition.X, _texturePosition.Y, traffic.GetTrafficType());
            Vector2 _outSideOfWorld = _exitLanePosition + toWardsCrossRoad.GetSpawnVector();

            // Set the way points for the car
            Vector2[] wayPoints = new Vector2[] 
            {
                _beforeTrafficlight,
                _sensorLocation,
                _exitLanePosition,
                _outSideOfWorld              
            };
            traffic.SetWayPoints(wayPoints);

            // Move the car to the first way point
            lock (_trafficLightQueue)
            {
                _trafficLightQueue.Add(traffic);
            }
            _trafficLightQueueLength++;
        }


        /// <summary>
        /// Add a pedestrian: special for the pedestrian traffic light
        /// </summary>
        /// <param name="traffic">A traffic type</param>
        public virtual void AddPedestrian(Traffic traffic, CrossRoadDirection toWardsCrossRoad, TrafficLight[] usedTrafficLights, bool spawned)
        {
        }


        /// <summary>
        /// Remove a vehicle from the queue
        /// </summary>
        public void RemoveTraffic(Traffic traffic)
        {
            lock (_trafficLightQueue)
            {
                _trafficLightQueueLength--;
                _trafficLightQueue.Remove(traffic);
            }
        }
        #endregion


        #region DrawableComponent functionality
        /// <summary>
        /// Draws the traffic
        /// </summary>
        /// <param name="gameTime"></param>
        public void DrawTraffic(GameTime gameTime, SpriteBatch spriteBatch)
        {
            for (int i = 0; i < _trafficLightQueueLength; i++)
            {
                lock (_trafficLightQueue)
                {
                    _trafficLightQueue.ElementAt(i).Draw(gameTime, spriteBatch);
                }
            }
        }


        /// <summary>
        /// Draw the traffic lights
        /// </summary>
        /// <param name="gameTime">The used time to draw a frame</param>
        /// <param name="spriteBatch">Class to draw a simple sprite</param>
        public void DrawTrafficLight(GameTime gameTime, SpriteBatch spriteBatch)
        {
            // Draw the traffic light
            spriteBatch.Draw(_textures[0], _texturePosition, null, Color.White, _rotationInDegrees, _textureCenters[0], 1.0f, _spriteEffects, 0);
            spriteBatch.Draw(_textures[_trafficLightState], _texturePosition, null, Color.White, _rotationInDegrees, _textureCenters[_trafficLightState], 1.0f, _spriteEffects, 0);

            // Change the texture for blinking state
            if (_blinking)
            {
                if (_blinkingCounter > 30)
                {
                    _blinkingCounter = 0;

                    if (_trafficLightState != _blinkLightState)
                    {
                        _tempStateStorage = _trafficLightState;
                        _trafficLightState = _blinkLightState;
                    }
                    else
                    {
                        _trafficLightState = _tempStateStorage;
                    }
                }
                _blinkingCounter++;
            }
        }
        #endregion
    }
}
